package com.wolfsea.launcherdemo
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.view.get

/**
 *@desc LauncherItemLayout
 *@author liuliheng
 *@time 2022/12/26  10:28
 **/
class LauncherItemLayout @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null
) : ViewGroup(context, attributeSet) {

    private val horizontalSpace = context.resources.getDimension(R.dimen.dp_20).toInt()
    private val leftRightSpace = horizontalSpace

    private var downX = 0
    private var flagDownX = 0
    private var downY = 0
    private var flagDownY = 0
    private var dragTotalMovedX = 0
    private var dragTotalMovedY = 0

    private var originChildIndex = -1
    private var moveChildIndex = -1
    private var finalChildIndex = -1
    private var finalChildTranslateX = 0F
    private var finalChildTranslateY = 0F

    private var itemDragging = false

    private var dragView: View? = null

    private val viewMap by lazy {
        mutableMapOf<Int, View>()
    }

    private val itemRectMap by lazy {
        mutableMapOf<Int, Rect>()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        for (i in 0.until(childCount)) {
            val child = get(i)
            val childMeasureWidth = child.measuredWidth
            val layoutParams = child.layoutParams as MarginLayoutParams
            val itemSpace = ((measuredWidth - 2 * leftRightSpace - BaseConstant.ITEMS_PER_ROW * childMeasureWidth).toFloat() / BaseConstant.ITEMS_PER_ROW / 2).toInt()
            layoutParams.leftMargin = itemSpace
            layoutParams.rightMargin = itemSpace
            child.layoutParams = layoutParams
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)
        }

        setMeasuredDimension(
            MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd,
            MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom
        )
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        var j = 0
        var calcIndex = 0
        for (i in 0.until(childCount)) {
            val child = get(i)
            val childMeasuredWidth = child.measuredWidth
            val childMeasuredHeight = child.measuredHeight
            val layoutParams = child.layoutParams as MarginLayoutParams
            val topMargin = layoutParams.topMargin
            val leftMargin = layoutParams.leftMargin
            val rightMargin = layoutParams.rightMargin

            if (i != 0 && i % BaseConstant.ITEMS_PER_ROW == 0) {
                j++
                calcIndex = 0
            }

            val baseCalcLRResult =
                leftRightSpace + leftMargin * (calcIndex + 1) + rightMargin * calcIndex
            val cl = baseCalcLRResult + childMeasuredWidth * calcIndex
            val ct = topMargin * (j + 1) + childMeasuredHeight * j
            val cr = cl + childMeasuredWidth
            val cb = ct + childMeasuredHeight

            child.layout(
                cl,
                ct,
                cr,
                cb
            )

            if (itemRectMap[i] == null) {
                itemRectMap[i] = Rect().apply { set(cl, ct, cr, cb) }
            }

            calcIndex++
        }
    }

    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(LauncherLayout.TAG,"onInterceptTouchEvent---MotionEvent.ACTION_DOWN")
                flagDownX = ev.x.toInt()
                downX = flagDownX
                downY = ev.y.toInt()
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(LauncherLayout.TAG,"onInterceptTouchEvent---MotionEvent.ACTION_MOVE")
                val actionMoveX = ev.x.toInt()
                if (Math.abs(actionMoveX - downX) >= BaseConstant.MIN_SLIDE_DISTANCE) {
                    downX = actionMoveX
                    return true
                }
            }
        }
        return super.onInterceptTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        performClick()
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                flagDownX = event.x.toInt()
                downX = flagDownX
                flagDownY = event.y.toInt()
                downY = flagDownY
            }
            MotionEvent.ACTION_MOVE -> {
                //actionMoveItemDrag(event)
            }
            MotionEvent.ACTION_UP -> {
                //actionUpItemDrag(event)
            }
        }
        return true
    }

    override fun performClick(): Boolean {
        return super.performClick()
    }

    fun addContent(contentDataItem: ContentData, i: Int) {
        val layout = LayoutInflater.from(context).inflate(R.layout.layout_content, this, false)
        val llContent = layout.findViewById<LinearLayoutCompat>(R.id.ll_content)
        llContent.tag = i
        viewMap[i] = llContent
        llContent.setOnClickListener {
            context.showToast(contentDataItem.contentDesc)
        }
        llContent.setOnLongClickListener {
            originChildIndex = it.tag as Int
            finalChildIndex = originChildIndex
            dragView = it
            itemDragging = true
            return@setOnLongClickListener true
        }
        val contentIv = layout.findViewById<AppCompatImageView>(R.id.iv_content)
        contentIv.setBackgroundResource(contentDataItem.drawRes)
        val contentTv = layout.findViewById<AppCompatTextView>(R.id.tv_content)
        contentTv.text = contentDataItem.contentDesc
        addView(
            layout,
            MarginLayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT
            ).apply {
                topMargin = context.resources.getDimension(R.dimen.dp_20).toInt()
            }
        )
    }

    fun launcherItemDragging(): Boolean = itemDragging

    private fun actionMoveItemDrag(event: MotionEvent) {
        val actionMovedX = event.x.toInt()
        val actionMovedY = event.y.toInt()
        moveChildIndex = getChildIndex(actionMovedX, actionMovedY)
        Log.d(TAG, "action_move--childIndex:${moveChildIndex}")
        val diffMovedX = actionMovedX - downX
        val diffMovedY = actionMovedY - downY
        dragTotalMovedX += diffMovedX
        dragTotalMovedY += diffMovedY
        dragView?.drag2(dragTotalMovedX.toFloat(), dragTotalMovedY.toFloat())

        if (originChildIndex != moveChildIndex && moveChildIndex != -1) {
            val targetView = viewMap[moveChildIndex]
            val layoutParams = targetView?.layoutParams as? MarginLayoutParams
            if (Math.abs(diffMovedX) > Math.abs(diffMovedY)) {
                val translationX = (layoutParams?.leftMargin ?: 0) + (layoutParams?.rightMargin
                    ?: 0) + (targetView?.measuredWidth ?: 0)
                val translationValue = translationX.toFloat() * if (diffMovedX > 0) -1 else 1
                targetView?.apply {
                    drag2(
                        translationValue,
                        0F,
                        0F
                    )
                }
                finalChildTranslateX += translationValue
            } else {
                val translationY =
                    (layoutParams?.topMargin ?: 0) + (targetView?.measuredHeight ?: 0)
                val translationValue = translationY.toFloat() * if (diffMovedY > 0) -1 else 1
                targetView?.apply {
                    drag2(
                        0F,
                        translationValue,
                        0F
                    )
                }
                finalChildTranslateY += translationValue
            }
            originChildIndex = moveChildIndex
        }

        downX = actionMovedX
        downY = actionMovedY
    }

    private fun actionUpItemDrag(event: MotionEvent) {
        val actionUpDiffValueX = event.x.toInt() - downX
        val actionUpDiffValueY = event.y.toInt() - downY
        val targetView = viewMap[finalChildIndex]
        targetView?.apply {
            drag2(
                finalChildTranslateX * if (actionUpDiffValueX > 0) 1 else -1,
                finalChildTranslateY * if (actionUpDiffValueY > 0) 1 else -1,
                0F)
        }

        finalChildTranslateX = 0F
        finalChildTranslateY = 0F

        dragTotalMovedX = 0
        dragTotalMovedY = 0

        itemDragging = false
    }

    private fun getChildIndex(x:Int,y:Int): Int {
        for ((key, value) in itemRectMap) {
            if (value.contains(x, y)) {
                return key
            }
        }
        return -1
    }

    private fun View.drag(x: Float, y: Float, z: Float = x) {
        translationX += x
        translationY += y
        translationZ = Math.abs(z)
    }

    private fun View.drag2(x: Float, y: Float, z: Float = x) {
        translationX = x
        translationY = y
        translationZ = Math.abs(z)
    }

    private fun View.reset() {
        translationX = 0F
        translationY = 0F
        translationZ = 0F
    }

    companion object {
        const val TAG = "LauncherItemLayout"
    }

}